#pragma once

#include "DesignExtractor.h"

DesignExtractor::DesignExtractor(PKB* pkb) {
	myPKB = pkb;
};

void DesignExtractor::extractDesign() {
	myPKB->follows->computeClosure();
	myPKB->parent->computeClosure();
	unordered_set<INDEX> resultMod, resultUsed;
	INDEX_SET children, varModByChildren, varUsedByChildren;
	for (auto it = myPKB->ast->whileStmtTable.begin(); it != myPKB->ast->whileStmtTable.end(); ++it) {
		children.clear();
		resultMod.clear();
		resultUsed.clear();
		children = myPKB->parent->getIntSynClosure(it->first, "stmt", "nombre").project("nombre");
		for (auto it2 = children.begin(); it2 != children.end(); ++it2) {
			varModByChildren.clear();
			varUsedByChildren.clear();
			varModByChildren = myPKB->modifies->getModifiedByStmt(*it2);
			varUsedByChildren = myPKB->uses->getUsedIndexUses(*it2);
			for (auto it3 = varModByChildren.begin(); it3 != varModByChildren.end(); ++it3) {
				resultMod.insert(*it3);
			}
			for (auto it3 = varUsedByChildren.begin(); it3 != varUsedByChildren.end(); ++it3) {
				resultUsed.insert(*it3);
			}
		}
		for (auto it4 = resultMod.begin(); it4 != resultMod.end(); ++it4) {
			myPKB->modifies->setModifiesStmtModifies(it->first,*it4);
		}
		for (auto it4 = resultUsed.begin(); it4 != resultUsed.end(); ++it4) {
			myPKB->uses->setUsesStmtUses(it->first,*it4);
		}
	}

	//testing
	bool repeat = true;
	while(repeat)
	{
		repeat = false;
		for (auto it = myPKB->stmtTable->callsProcTable.begin(); it != myPKB->stmtTable->callsProcTable.end(); ++it) 
		{
			INDEX callingProcIndex = myPKB->procTable->getProcIndex(it->second);
			if(callingProcIndex==-1)
			{
				cout << "invalid proc" << endl;//must throw exception here!!
			} else {
				myPKB->calls->set(it->first,callingProcIndex);
				for (auto it2 = myPKB->modifies->modifiesTableProc.begin(); it2 !=myPKB->modifies->modifiesTableProc.end(); ++it2)
				{
					if(it2->first==callingProcIndex)
					{
						for (auto it3 = it2->second.begin(); it3 != it2->second.end(); ++it3)
						{
							for (auto it4 = myPKB->modifies->modifiesTableProc.begin(); it4 !=myPKB->modifies->modifiesTableProc.end(); ++it4)
							{
								if(it4->first==it->first)
								{
									int size = it4->second.size();
									it4->second.insert(*it3);
									int size2 = it4->second.size();
									if(size!=size2)
									{
										repeat = true;
									}
								}
							}
						}
					}
				}
			}
		}
	}

	for (auto it = myPKB->stmtTable->callsStmtTable.begin(); it != myPKB->stmtTable->callsStmtTable.end(); ++it) 
	{
		INDEX callingProcIndex = myPKB->procTable->getProcIndex(it->second);
		set<INDEX> parents = myPKB->parent->getSynIntClosure("stmt","p",it->first).project("p");
		for (auto it2 = myPKB->modifies->modifiesTableProc.begin(); it2 != myPKB->modifies->modifiesTableProc.end(); ++it2) 
		{
			if(it2->first==callingProcIndex)
			{
				for (auto it3 = it2->second.begin(); it3 != it2->second.end(); ++it3) 
				{
					myPKB->modifies->setModifiesStmtModifies(it->first,*it3);
					for (auto it4 = parents.begin(); it4 != parents.end(); ++it4) 
					{
						myPKB->modifies->setModifiesStmtModifies(*it4,*it3);
					}
				}
			}
		}
	}

	repeat = true;
	while(repeat)
	{
		repeat = false;
		for (auto it = myPKB->stmtTable->callsProcTable.begin(); it != myPKB->stmtTable->callsProcTable.end(); ++it) 
		{
			INDEX callingProcIndex = myPKB->procTable->getProcIndex(it->second);
			if(callingProcIndex==-1)
			{} else {
				for (auto it2 = myPKB->uses->usesTableProc.begin(); it2 !=myPKB->uses->usesTableProc.end(); ++it2)
				{
					if(it2->first==callingProcIndex)
					{
						for (auto it3 = it2->second.begin(); it3 != it2->second.end(); ++it3)
						{
							for (auto it4 = myPKB->uses->usesTableProc.begin(); it4 !=myPKB->uses->usesTableProc.end(); ++it4)
							{
								if(it4->first==it->first)
								{
									int size = it4->second.size();
									it4->second.insert(*it3);
									int size2 = it4->second.size();
									if(size!=size2)
									{
										repeat = true;
									}
								}
							}
						}
					}
				}
			}
		}
	}

	for (auto it = myPKB->stmtTable->callsStmtTable.begin(); it != myPKB->stmtTable->callsStmtTable.end(); ++it) 
	{
		INDEX callingProcIndex = myPKB->procTable->getProcIndex(it->second);
		set<INDEX> parents = myPKB->parent->getSynIntClosure("stmt","p",it->first).project("p");
		for (auto it2 = myPKB->uses->usesTableProc.begin(); it2 != myPKB->uses->usesTableProc.end(); ++it2) 
		{
			if(it2->first==callingProcIndex)
			{
				for (auto it3 = it2->second.begin(); it3 != it2->second.end(); ++it3) 
				{
					myPKB->uses->setUsesStmtUses(it->first,*it3);
					for (auto it4 = parents.begin(); it4 != parents.end(); ++it4) 
					{
						myPKB->uses->setUsesStmtUses(*it4,*it3);
					}
				}
			}
		}
	}

	for (auto it = myPKB->stmtTable->callsStmtTable.begin(); it != myPKB->stmtTable->callsStmtTable.end(); ++it) 
	{
		myPKB->calls->callsTableStmt.insert(make_pair<INDEX,INDEX>(it->first, myPKB->procTable->getProcIndex(it->second)));
	}				
	myPKB->calls->computeClosure();

	myPKB->follows->computeClosure();
	myPKB->parent->computeClosure();

};